using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.IO;
using System.Reflection;
using System.Windows.Forms;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
using OpenComposite.Base;

namespace OpenComposite.Mapper
{
	[ToolboxItem(false)]
	public partial class MapperControl : UserControl
	{
		#region Initialization

		public MapperControl()
		{
			InitializeComponent();

			init();
		}

		private void init()
		{
			treeTarget.MouseWheel += new MouseEventHandler(tree_MouseWheel);
			treeSource.MouseWheel += new MouseEventHandler(tree_MouseWheel);

			treeSource.ExpandAll();
			treeTarget.ExpandAll();

			_selectedMapItems = new List<MappingItem>();
			_mapperSettings = new MapperSettings();
			_xsltDoc = new XsltDocument();
		}

		#endregion

		#region Public Members

		#region Properties
		public MapperSettings MapperSettings
		{
			get { return _mapperSettings; }
		}
		#endregion

		#region Methods

		public void InitializeMapper(XmlSchema sourceSchema, XmlSchema targetSchema,
			string mapping)
		{
			this.InitializeMapper(sourceSchema, null, targetSchema, null, mapping);
		}

		public void InitializeMapper(XmlSchema sourceSchema, XmlSchemaSet sourceSet,
			XmlSchema targetSchema, XmlSchemaSet targetSet, string mapping)
		{
			_xsdSource = sourceSchema;
			_xsetSource = sourceSet;
			_xsdTarget = targetSchema;
			_xsetTarget = targetSet;

			buildSchemaTree(treeSource, _xsdSource, _xsetSource);
			buildSchemaTree(treeTarget, _xsdTarget, _xsetTarget);

			if ( mapping == null ) {
				automaticMapping(treeSource.Nodes, treeTarget.Nodes);
			}

			readMappingXml(mapping);
		}

		private void automaticMapping(TreeNodeCollection sourceNodes, TreeNodeCollection targetNodes)
		{
			// this is true if only one document is subscribed
			if ( sourceNodes.Count != 1 ) return;
			//if ( sourceNodes[0].Nodes.Count != 1 ) return;
			foreach ( TreeNode tNode in targetNodes ) {
				if ( tNode.Nodes.Count == 0 ) continue;
				foreach ( TreeNode sNode in sourceNodes ) {
					if ( sNode.Nodes.Count == 0 ) continue;
					TreeNodeInfos sInfo = sNode.Tag as TreeNodeInfos;
					TreeNodeInfos tInfo = tNode.Tag as TreeNodeInfos;
					if ( sInfo.Mappings.Count > 0 || tInfo.Mappings.Count > 0 ) break;
					if ( !string.IsNullOrEmpty(sInfo.DataType) &&
						 sInfo.DataType == tInfo.DataType ) {
						setNewMapping(sNode, tNode, false);
						break;
					}
					automaticMapping(sNode.Nodes, targetNodes);
				}
				automaticMapping(sourceNodes, tNode.Nodes);
			}
		}

		public XsltDocument GenerateXslt(out string mapping)
		{
			if ( treeSource.Nodes.Count > 0 ) {
				TreeNodeInfos tniSource = treeSource.Nodes[0].Tag as TreeNodeInfos;
				if ( tniSource.Mappings.Count > 0 ) {
					TreeNodeInfos tniTarget;
					foreach ( MappingItem mi in tniSource.Mappings ) {
						tniTarget = mi.TargetNode.Tag as TreeNodeInfos;
						if ( tniTarget != null ) tniTarget.Mappings.Remove(mi);
						_mapperSettings.MappingItems.Remove(mi);
					}
					tniSource.Mappings.Clear();
					_selectedMapItems.Clear();
					treeSource.SelectedNode = null;
					treeTarget.SelectedNode = null;
					this.Refresh();
				}
			}
			if ( treeTarget.Nodes.Count > 0 ) {
				TreeNodeInfos tniTarget = treeTarget.Nodes[0].Tag as TreeNodeInfos;
				if ( tniTarget.Mappings.Count > 0 ) {
					TreeNodeInfos tniSource;
					foreach ( MappingItem mi in tniTarget.Mappings ) {
						tniSource = mi.SourceNode.Tag as TreeNodeInfos;
						if ( tniSource != null ) tniSource.Mappings.Remove(mi);
						_mapperSettings.MappingItems.Remove(mi);
					}
					tniTarget.Mappings.Clear();
					_selectedMapItems.Clear();
					treeTarget.SelectedNode = null;
					treeSource.SelectedNode = null;
					this.Refresh();
				}
			}
			XsltDocument xsltDoc = null;
			mapping = writeMappingXml();

			if ( !string.IsNullOrEmpty(mapping) && treeSource.Nodes.Count > 0 && treeTarget.Nodes.Count > 0 ) {
				XsltGenerator xsltGen = new XsltGenerator();
				xsltGen.SourceRootTreeNode = treeSource.Nodes[0];
				xsltGen.SourceSchemaNamespace = _xsdSource.TargetNamespace;
				xsltGen.SourceUri = _xsdSource.SourceUri;
				xsltGen.TargetRootTreeNode = treeTarget.Nodes[0];
				xsltGen.TargetSchemaNamespace = _xsdTarget.TargetNamespace;
				xsltGen.TargetUri = _xsdTarget.SourceUri;

				xsltDoc = xsltGen.GenerateXsltSourceToTarget2();
			}
			return xsltDoc;
		}

		public string GenerateCSharpMapping()
		{
			return "";
		}

		#endregion

		#endregion

		#region Private Members

		#region Enums

		enum TreeViewImages { Schema, Element, SimpleType, ComplexType, Annotation, Documentation, AppInfo, Attribute, Facet };

		#endregion

		#region Variables

		private MapperSettings _mapperSettings;
		private List<MappingItem> _selectedMapItems;
		//private List<XmlNode> _lstXsltTargetNodes;

		private XmlSchema _xsdSource = null;
		private XmlSchema _xsdTarget = null;
		private XmlSchemaSet _xsetSource = null;
		private XmlSchemaSet _xsetTarget = null;

		//private TreeNode _tnSourceRoot = null;
		//private TreeNode _tnTargetRoot = null;

		private XsltDocument _xsltDoc = null;

		private bool _bIsInAfterSelect = false;

		private TreeNode _tnDrag = null;
		private BitmapCursor _bmpcurDrag = null;

		private string _errorMessage = "";

		private Dictionary<string, string> _dicPrefixNamespace = null;
		#endregion

		#region Event Handlers

		private void tableLayoutMapping_CellPaint(object sender, TableLayoutCellPaintEventArgs e)
		{
			if ( e.Column != 1 )
				return;

			foreach ( MappingItem mi in _mapperSettings.MappingItems ) {
				Point ptStart, ptEnd;
				if ( mi.SourceNode.IsVisible )
					ptStart = new Point(e.CellBounds.Left, mi.SourceNode.Bounds.Top + mi.SourceNode.Bounds.Height / 2);
				else {
					TreeNode tnParent = searchVisibleParent(mi.SourceNode);
					ptStart = new Point(e.CellBounds.Left, tnParent.Bounds.Top + tnParent.Bounds.Height / 2);
				}

				if ( mi.TargetNode.IsVisible )
					ptEnd = new Point(e.CellBounds.Right, mi.TargetNode.Bounds.Top + mi.TargetNode.Bounds.Height / 2);
				else {
					TreeNode tnParent = searchVisibleParent(mi.TargetNode);
					ptEnd = new Point(e.CellBounds.Right, tnParent.Bounds.Top + tnParent.Bounds.Height / 2);
				}
				if ( ptStart.Y < e.CellBounds.Top ) ptStart.Y = e.CellBounds.Top + 1;
				if ( ptEnd.Y < e.CellBounds.Top ) ptEnd.Y = e.CellBounds.Top + 1;
				if ( ptStart.Y > e.CellBounds.Bottom ) ptStart.Y = e.CellBounds.Bottom - 2;
				if ( ptEnd.Y > e.CellBounds.Bottom ) ptEnd.Y = e.CellBounds.Bottom - 2;

				mi.ConnectionPath.Reset();
				mi.ConnectionPath.AddLine(ptStart, ptEnd);
				e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
				if ( mi.SourceNode.IsSelected && mi.TargetNode.IsSelected ) {
					using ( Pen p = new Pen(Color.Red) ) {
						p.Width = 2;
						e.Graphics.DrawPath(p, mi.ConnectionPath);
					}
				} else
					e.Graphics.DrawPath(SystemPens.GrayText, mi.ConnectionPath);
				e.Graphics.SmoothingMode = SmoothingMode.Default;
			}
		}

		private void tableLayoutMapping_MouseClick(object sender, MouseEventArgs e)
		{
			bool lineClicked = false;
			using ( Pen penOutline = new Pen(Color.Black, 5) ) {
				foreach ( MappingItem mi in _mapperSettings.MappingItems ) {
					if ( mi.ConnectionPath.IsOutlineVisible(e.X, e.Y, penOutline) ) {
						treeSource.SelectedNode = mi.SourceNode;
						treeTarget.SelectedNode = mi.TargetNode;
						tableLayoutMapping.Invalidate();
						if ( ( Control.ModifierKeys & Keys.Control ) == 0 )
							_selectedMapItems.Clear();
						_selectedMapItems.Add(mi);
						if ( e.Button == MouseButtons.Right ) {
							miDeleteAssignment.Visible = true;
							contextAssignment.Show((Control)sender, e.X, e.Y);
						}
						lineClicked = true;
						break;
					}
				}
			}
			if ( !lineClicked && e.Button == MouseButtons.Right ) {
				miDeleteAssignment.Visible = false;
				contextAssignment.Show((Control)sender, e.X, e.Y);
			}
		}

		#region Drag'n'Drop Source Tree

		private void treeSource_ItemDrag(object sender, ItemDragEventArgs e)
		{
			TreeNode tn = e.Item as TreeNode;
			if ( tn != null ) {
				_tnDrag = tn;
				//_bmpcurDrag = createDragCursor(DragDropEffects.Link, tn);
				_bmpcurDrag = Helper.CreateDragCursorFromTreeNode(this, DragDropEffects.Link, tn);
				treeSource.SelectedNode = tn;
			}
			treeSource.DoDragDrop(e.Item, DragDropEffects.Link | DragDropEffects.Move);
		}
		private void treeSource_DragDrop(object sender, DragEventArgs e)
		{
			hideMapDiffToolTip(treeSource);
			TreeNode tnTarget = e.Data.GetData(typeof(TreeNode)) as TreeNode;
			if ( tnTarget == null || tnTarget.TreeView != treeTarget ) return;	// EXIT

			TreeViewHitTestInfo hit = treeSource.HitTest(treeSource.PointToClient(new Point(e.X, e.Y)));
			if ( hit.Node == null ) return;	// EXIT

			if ( !checkCompatibleNodes(tnTarget, hit.Node) ) {
				DialogResult r = MessageBox.Show(
					string.Format(
						"You are attempting to perform a data mapping between two different data types. " +
						"This could cause a problem with the generated XSLT.{0}{0}" +
						"Do you really wish to create this data mapping?",
						Environment.NewLine),
					"Mapping", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
				if ( r == DialogResult.No ) return;
			}

			setNewMapping(hit.Node, tnTarget, false);
		}
		private void treeSource_DragEnter(object sender, DragEventArgs e)
		{

		}
		private void treeSource_DragLeave(object sender, EventArgs e)
		{
			hideMapDiffToolTip(treeSource);
		}
		private void treeSource_DragOver(object sender, DragEventArgs e)
		{
			TreeNode tn = e.Data.GetData(typeof(TreeNode)) as TreeNode;
			if ( tn == null || tn.TreeView == treeSource )
				e.Effect = DragDropEffects.None;
			else {
				TreeViewHitTestInfo hit = treeSource.HitTest(treeSource.PointToClient(new Point(e.X, e.Y)));
				if ( hit.Node != null ) {
					treeSource.SelectedNode = hit.Node;
					if ( !checkCompatibleNodes(tn, hit.Node) ) {
						showMapDiffToolTip(hit);
					} else {
						hideMapDiffToolTip(treeSource);
					}
					e.Effect = DragDropEffects.Link;
				} else {
					hideMapDiffToolTip(treeSource);
					e.Effect = DragDropEffects.None;
				}
			}
		}
		private void treeSource_GiveFeedback(object sender, GiveFeedbackEventArgs e)
		{
			if ( _bmpcurDrag != null ) {
				if ( e.Effect == DragDropEffects.None ) {
					e.UseDefaultCursors = true;
					return;
				}
				e.UseDefaultCursors = false;
				if ( Cursor.Current != _bmpcurDrag.Cursor )
					Cursor.Current = _bmpcurDrag.Cursor;
			} else {
				e.UseDefaultCursors = true;
			}
		}
		private void treeSource_QueryContinueDrag(object sender, QueryContinueDragEventArgs e)
		{
			if ( e.Action != DragAction.Continue ) {
				_tnDrag = null;
				if ( _bmpcurDrag != null ) {
					_bmpcurDrag.Dispose();
					_bmpcurDrag = null;
				}
			}
		}

		#endregion

		#region Drag'n'Drop Target Tree

		private void treeTarget_ItemDrag(object sender, ItemDragEventArgs e)
		{
			TreeNode tn = e.Item as TreeNode;
			if ( tn != null ) {
				_tnDrag = tn;
				//_bmpcurDrag = createDragCursor(DragDropEffects.Link, tn);
				_bmpcurDrag = Helper.CreateDragCursorFromTreeNode(this, DragDropEffects.Link, tn);
				treeTarget.SelectedNode = tn;
			}
			treeTarget.DoDragDrop(e.Item, DragDropEffects.Link | DragDropEffects.Move);
		}
		private void treeTarget_DragDrop(object sender, DragEventArgs e)
		{
			hideMapDiffToolTip(treeTarget);
			TreeNode tnSource = e.Data.GetData(typeof(TreeNode)) as TreeNode;
			if ( tnSource == null || tnSource.TreeView != treeSource ) return;	// EXIT

			TreeViewHitTestInfo hit = treeTarget.HitTest(treeTarget.PointToClient(new Point(e.X, e.Y)));
			if ( hit.Node == null ) return;	// EXIT

			if ( !checkCompatibleNodes(tnSource, hit.Node) ) {
				DialogResult r = MessageBox.Show(
					string.Format(
						"You are attempting to perform a data mapping between two different data types. " +
						"This could cause a problem with the generated XSLT.{0}{0}" +
						"Do you really wish to create this data mapping?",
						Environment.NewLine),
					"Mapping", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
				if ( r == DialogResult.No ) return;	// EXIT
			}

			setNewMapping(tnSource, hit.Node, false);
		}
		private void treeTarget_DragEnter(object sender, DragEventArgs e)
		{
		}
		private void treeTarget_DragLeave(object sender, EventArgs e)
		{
			hideMapDiffToolTip(treeTarget);
		}
		private void treeTarget_DragOver(object sender, DragEventArgs e)
		{
			TreeNode tn = e.Data.GetData(typeof(TreeNode)) as TreeNode;
			if ( tn == null || tn.TreeView == treeTarget )
				e.Effect = DragDropEffects.None;
			else {
				Point ptClient = treeTarget.PointToClient(new Point(e.X, e.Y));
				TreeViewHitTestInfo hit = treeTarget.HitTest(ptClient);
				if ( hit.Node != null ) {
					treeTarget.SelectedNode = hit.Node;
					if ( !checkCompatibleNodes(tn, hit.Node) ) {
						showMapDiffToolTip(hit);
					} else {
						hideMapDiffToolTip(treeTarget);
					}
					e.Effect = DragDropEffects.Link;
				} else {
					hideMapDiffToolTip(treeTarget);
					e.Effect = DragDropEffects.None;
				}
			}
		}

		private void treeTarget_GiveFeedback(object sender, GiveFeedbackEventArgs e)
		{
			if ( _bmpcurDrag != null ) {
				if ( e.Effect == DragDropEffects.None ) {
					e.UseDefaultCursors = true;
					return;
				}
				e.UseDefaultCursors = false;
				if ( Cursor.Current != _bmpcurDrag.Cursor )
					Cursor.Current = _bmpcurDrag.Cursor;
			} else {
				e.UseDefaultCursors = true;
			}
		}
		private void treeTarget_QueryContinueDrag(object sender, QueryContinueDragEventArgs e)
		{
			if ( e.Action != DragAction.Continue ) {
				_tnDrag = null;
				if ( _bmpcurDrag != null ) {
					_bmpcurDrag.Dispose();
					_bmpcurDrag = null;
				}
			}
		}

		#endregion

		private void tableLayoutMapping_GiveFeedback(object sender, GiveFeedbackEventArgs e)
		{
			if ( _bmpcurDrag != null ) {
				e.UseDefaultCursors = false;
				if ( Cursor.Current != _bmpcurDrag.Cursor )
					Cursor.Current = _bmpcurDrag.Cursor;
			} else {
				e.UseDefaultCursors = true;
			}
		}
		private void tableLayoutMapping_DragOver(object sender, DragEventArgs e)
		{
			e.Effect = DragDropEffects.Move;
		}

		private void treeSource_DrawNode(object sender, DrawTreeNodeEventArgs e)
		{
			drawTreeNode((TreeView)sender, e);
		}
		private void treeTarget_DrawNode(object sender, DrawTreeNodeEventArgs e)
		{
			drawTreeNode((TreeView)sender, e);
		}

		private void treeSource_Scroll(object sender, ScrollEventArgs e)
		{
			if ( e.ScrollOrientation == ScrollOrientation.VerticalScroll ) {
				tableLayoutMapping.Invalidate();
			}
		}
		private void treeTarget_Scroll(object sender, ScrollEventArgs e)
		{
			if ( e.ScrollOrientation == ScrollOrientation.VerticalScroll ) {
				tableLayoutMapping.Invalidate();
			}
		}

		private void tree_AfterCollapse(object sender, TreeViewEventArgs e)
		{
			tableLayoutMapping.Invalidate();
		}
		private void tree_AfterExpand(object sender, TreeViewEventArgs e)
		{
			tableLayoutMapping.Invalidate();
		}

		private void tree_MouseWheel(object sender, MouseEventArgs e)
		{
			tableLayoutMapping.Invalidate();
		}

		private void tree_AfterSelect(object sender, TreeViewEventArgs e)
		{
			if ( _bIsInAfterSelect )
				return;

			try {
				_bIsInAfterSelect = true;
				TreeNodeInfos tni = e.Node.Tag as TreeNodeInfos;
				if ( tni == null || tni.Mappings.Count == 0 ) {
					_selectedMapItems.Clear();
					if ( sender == treeSource )
						treeTarget.SelectedNode = null;
					if ( sender == treeTarget )
						treeSource.SelectedNode = null;
					tableLayoutMapping.Invalidate();
					return;
				}
				_selectedMapItems.Clear();
				_selectedMapItems.AddRange(tni.Mappings);

				if ( sender == treeSource )
					treeTarget.SelectedNode = tni.Mappings[0].TargetNode;
				if ( sender == treeTarget )
					treeSource.SelectedNode = tni.Mappings[0].SourceNode;

				tableLayoutMapping.Invalidate();
			} catch ( Exception ex ) {
				Debug.WriteLine(ex.ToString());
			} finally {
				_bIsInAfterSelect = false;
			}
		}

		private void SchemaValidationCallback(object sender, ValidationEventArgs e)
		{
			//MessageBox.Show(e.Message);
			if ( !string.IsNullOrEmpty(_errorMessage) ) _errorMessage += Environment.NewLine;
			if ( e.Severity == XmlSeverityType.Error ) {
				_errorMessage += "Error: " + e.Message;
			}
		}

		private void tree_NodeMouseHover(object sender, TreeNodeMouseHoverEventArgs e)
		{
			toolTips.Tag = null;
			toolTips.ToolTipIcon = ToolTipIcon.None;
			toolTips.ToolTipTitle = "";
			toolTips.Show(e.Node.ToolTipText,
				e.Node.TreeView, e.Node.Bounds.Left, e.Node.Bounds.Bottom);
		}
		private void tree_MouseLeave(object sender, EventArgs e)
		{
			TreeView tv = sender as TreeView;
			if ( tv != null ) {
				toolTips.Hide(tv);
			}
		}

		private void miDeleteAssignment_Click(object sender, EventArgs e)
		{
			if ( _selectedMapItems.Count == 0 ) return;

			TreeNodeInfos tni;

			foreach ( MappingItem mi in _selectedMapItems ) {
				tni = mi.SourceNode.Tag as TreeNodeInfos;
				if ( tni != null ) tni.Mappings.Remove(mi);
				tni = mi.TargetNode.Tag as TreeNodeInfos;
				if ( tni != null ) tni.Mappings.Remove(mi);
				_mapperSettings.MappingItems.Remove(mi);
			}
			_selectedMapItems.Clear();
			treeSource.SelectedNode = null;
			treeTarget.SelectedNode = null;
			tableLayoutMapping.Invalidate();
		}
		private void miDeleteAll_Click(object sender, EventArgs e)
		{
			if ( MessageBox.Show("Do you really want to remove all mappings?", "Delete All Mappings?",
					MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No ) {
				return;	// EXIT: cancel delete all
			}
			TreeNodeInfos tni;
			foreach ( MappingItem mi in _mapperSettings.MappingItemArray ) {
				tni = mi.SourceNode.Tag as TreeNodeInfos;
				if ( tni != null ) tni.Mappings.Remove(mi);
				tni = mi.TargetNode.Tag as TreeNodeInfos;
				if ( tni != null ) tni.Mappings.Remove(mi);
				_mapperSettings.MappingItems.Remove(mi);
			}
			_selectedMapItems.Clear();
			treeSource.SelectedNode = null;
			treeTarget.SelectedNode = null;
			this.Refresh();
		}

		#endregion

		#region Methods

		/// <summary>
		/// Draw the TreeNode
		/// </summary>
		/// <param name="tv">TreeView</param>
		/// <param name="e">DrawTreeNodeEventArgs</param>
		private void drawTreeNode(TreeView tv, DrawTreeNodeEventArgs e)
		{
			if ( !e.Graphics.IsVisible(e.Bounds) ) {
				return;	// EXIT
			}

			Rectangle bounds = e.Bounds;

			string datatype = null;
			int dtypewidth = 0;
			TreeNodeInfos tni = e.Node.Tag as TreeNodeInfos;
			if ( tni != null ) {
				if ( string.IsNullOrEmpty(tni.DataType) ) {
					XmlSchemaSimpleType st = tni.SchemaObject as XmlSchemaSimpleType;
					if ( st != null ) {
						tni.DataType = st.Datatype.TypeCode.ToString();
					}
				}
				datatype = tni.DataType;

				if ( !string.IsNullOrEmpty(datatype) ) {
					datatype = " (" + datatype + ")";
				}
				//if ( !string.IsNullOrEmpty(tni.Namespace) ) {
				//    if ( datatype.Length > 0 ) datatype += " ";
				//    datatype += tni.Namespace;
				//}
				if ( !string.IsNullOrEmpty(datatype) ) {
					dtypewidth = (int)Math.Ceiling(e.Graphics.MeasureString(datatype, tv.Font).Width);
					bounds.Width += dtypewidth;
				}
			}
			Rectangle rectClear = new Rectangle(e.Bounds.X, e.Bounds.Y, e.Bounds.Width + 1, e.Bounds.Height);
			e.Graphics.FillRectangle(SystemBrushes.Window, rectClear);

			// Draw the background and node text for a selected node.
			if ( ( e.State & TreeNodeStates.Selected ) != 0 ) {
				// Draw the background of the selected node. The NodeBounds
				// method makes the highlight rectangle large enough to
				// include the text of a node tag, if one is present.
				Rectangle rectBG = new Rectangle(bounds.X, bounds.Y, bounds.Width, bounds.Height - 1);
				e.Graphics.FillRectangle(SystemBrushes.Highlight, rectBG/*Rectangle.Inflate(focusBounds,-1,-1)*/);

				// Retrieve the node font. If the node font has not been set,
				// use the TreeView font.
				Font nodeFont = e.Node.NodeFont;
				if ( nodeFont == null ) nodeFont = tv.Font;

				// Draw the node text.
				Rectangle rectText = bounds;// Rectangle.Inflate(e.Node.Bounds, 2, 0);
				rectText.Width -= ( dtypewidth - 1 );
				e.Graphics.DrawString(e.Node.Text, nodeFont, SystemBrushes.HighlightText, rectText, StringFormat.GenericTypographic);
				rectText.X = rectText.Right;
				rectText.Width = dtypewidth;
				e.Graphics.DrawString(datatype, nodeFont, SystemBrushes.HighlightText, rectText, StringFormat.GenericTypographic);
			} else {
				// Retrieve the node font. If the node font has not been set,
				// use the TreeView font.
				Font nodeFont = e.Node.NodeFont;
				if ( nodeFont == null ) nodeFont = tv.Font;

				// Draw the node text.
				Rectangle rectText = bounds;// Rectangle.Inflate(e.Node.Bounds, 2, 0);
				rectText.Width -= ( dtypewidth - 1 );
				using ( StringFormat sf = new StringFormat(StringFormat.GenericTypographic) ) {
					e.Graphics.DrawString(e.Node.Text, nodeFont, SystemBrushes.ControlText, rectText, sf);
					rectText.X = rectText.Right;
					rectText.Width = dtypewidth;
					e.Graphics.DrawString(datatype, nodeFont, SystemBrushes.GrayText, rectText, sf);
				}
			}

			//// If a node tag is present, draw its string representation 
			//// to the right of the label text.
			//if ( e.Node.Tag != null ) {
			//    e.Graphics.DrawString(e.Node.Tag.ToString(), tv.Font,
			//        Brushes.Black, e.Node.Bounds.Right + 2, e.Bounds.Top);
			//}

			// Draw the border and lines for the mapping
			if ( tni != null && tni.Mappings.Count > 0 ) {
				using ( Pen pen = new Pen(e.Node.IsSelected ? Color.Red : SystemColors.GrayText) ) {
					Rectangle rectMap = bounds;
					rectMap.Width += rectMap.Left - 6;
					rectMap.Location = new Point(6, rectMap.Top);
					rectMap.Height -= 2;
					e.Graphics.DrawRectangle(pen, rectMap);

					if ( ( e.State & TreeNodeStates.Focused ) != 0 ) {
						using ( Pen focusPen = new Pen(Color.Black) ) {
							focusPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;
							e.Graphics.DrawRectangle(focusPen, rectMap);
						}
					}

					Point ptStart, ptEnd;
					pen.Width = ( e.Node.IsSelected ? 2 : 1 );
					if ( tv == treeSource ) {
						ptStart = new Point(rectMap.Right, e.Node.Bounds.Top + e.Node.Bounds.Height / 2);
						ptEnd = new Point(tv.Right, e.Node.Bounds.Top + e.Node.Bounds.Height / 2);
					} else {
						ptStart = new Point(0, e.Node.Bounds.Top + e.Node.Bounds.Height / 2);
						ptEnd = new Point(rectMap.Left, e.Node.Bounds.Top + e.Node.Bounds.Height / 2);
					}
					e.Graphics.DrawLine(pen, ptStart, ptEnd);
				}
			} else {
				// If the node has focus, draw the focus rectangle large, making
				// it large enough to include the text of the node tag, if present.
				if ( ( e.State & TreeNodeStates.Focused ) != 0 ) {
					using ( Pen focusPen = new Pen(Color.Black) ) {
						focusPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;
						bounds.Width--;
						bounds.Height--;
						e.Graphics.DrawRectangle(focusPen, bounds);
					}
				}
			}
		}

		/// <summary>
		/// Returns the bounds of the specified node, including the region
		/// occupied by the node label and any node tag displayed.
		/// </summary>
		private Rectangle nodeBounds(TreeView tv, TreeNode node)
		{
			// Set the return value to the normal node bounds.
			Rectangle bounds = node.Bounds;
			//if ( node.Tag != null ) {
			//    // Retrieve a Graphics object from the TreeView handle
			//    // and use it to calculate the display width of the tag.
			//    using ( Graphics g = tv.CreateGraphics() ) {
			//        int tagWidth = (int)g.MeasureString
			//            (node.Tag.ToString(), tv.Font).Width + 6;

			//        // Adjust the node bounds using the calculated value.
			//        bounds.Offset(tagWidth / 2, 0);
			//        bounds = Rectangle.Inflate(bounds, tagWidth / 2, 0);
			//    }
			//}
			return bounds;
		}

		private void drawParentLine(DrawTreeNodeEventArgs e, TreeNode current, Pen pen)
		{
			if ( current.Nodes.Count > 0 && current.IsExpanded ) {
				int x = current.Bounds.Left + current.TreeView.Indent / 2;
				Rectangle rectLastChild = current.Nodes[current.Nodes.Count - 1].Bounds;
				e.Graphics.DrawLine(pen, x, e.Node.Bounds.Bottom, x,
					rectLastChild.Top + rectLastChild.Height / 2);
			}
			if ( current.Parent != null )
				drawParentLine(e, current.Parent, pen);
		}

		private void setNewMapping(TreeNode tnSource, TreeNode tnTarget, bool isLoading)
		{
			this.SuspendLayout();
			treeSource.BeginUpdate();
			treeTarget.BeginUpdate();

			setMapping(tnSource, tnTarget, isLoading);

			treeSource.EndUpdate();
			treeTarget.EndUpdate();
			this.ResumeLayout(true);
		}
		private void setMapping(TreeNode tnSource, TreeNode tnTarget, bool isLoading)
		{
			MappingItem mi = new MappingItem();
			mi.SourceNode = tnSource;
			mi.TargetNode = tnTarget;
			MappingItem miFind = _mapperSettings.MappingItems.Find(delegate(MappingItem item)
			{
				return ( mi.SourceNode == item.SourceNode && mi.TargetNode == item.TargetNode );
			});
			if ( miFind == null ) {
				setTreeNodeInfosMapping(tnSource, mi);
				setTreeNodeInfosMapping(tnTarget, mi);

				_mapperSettings.MappingItems.Add(mi);

				if ( !isLoading )
					connectChildNodes(tnSource, tnTarget);
			}
			treeSource.SelectedNode = tnSource;
			treeTarget.SelectedNode = tnTarget;
		}

		private void connectChildNodes(TreeNode tnSource, TreeNode tnTarget)
		{
			foreach ( TreeNode tnCS in tnSource.Nodes ) {
				foreach ( TreeNode tnCT in tnTarget.Nodes ) {
					string source = tnCS.Text.ToLower().Replace("_", "");
					string target = tnCT.Text.ToLower().Replace("_", "");
					if ( source.Equals(target) ) {
						TreeNodeInfos sInfo = tnCS.Tag as TreeNodeInfos;
						TreeNodeInfos tInfo = tnCT.Tag as TreeNodeInfos;
						if ( sInfo.Mappings.Count > 0 || tInfo.Mappings.Count > 0 ) break;
						setMapping(tnCS, tnCT, false);
					}
				}
			}
		}
		private void setTreeNodeInfosMapping(TreeNode tn, MappingItem mi)
		{
			if ( tn.Tag is TreeNodeInfos )
				( (TreeNodeInfos)tn.Tag ).Mappings.Add(mi);
			else
				tn.Tag = new TreeNodeInfos("", mi);
		}

		private TreeNode searchVisibleParent(TreeNode treeNode)
		{
			if ( treeNode.Parent == null )
				return treeNode;
			if ( treeNode.Parent.IsVisible )
				return treeNode.Parent;
			return searchVisibleParent(treeNode.Parent);
		}

		private void buildSchemaTree(TreeViewEx tv, XmlSchema xsd, XmlSchemaSet xset)
		{
			_mapperSettings.MappingItems.Clear();
			_selectedMapItems.Clear();

			tv.BeginUpdate();
			tv.Nodes.Clear();
			//TreeNode rootNode = new TreeNode("Schema:");
			//tv.Nodes.Add(rootNode);
			//rootNode.ImageIndex = (int)TreeViewImages.Schema;
			//rootNode.SelectedImageIndex = (int)TreeViewImages.Schema;
			//cbGlobalTypes.Items.Clear();

			if ( xset == null ) {
				CompileSchema(xsd, null);
				DecodeSchema(xsd, null/*rootNode*/, tv);
			} else {
				CompileSchema(xsd, xset);
				DecodeSchema(xset, xsd, null, tv);
			}

			tv.ExpandAll();
			tv.EndUpdate();
		}

		private void CompileSchema(XmlSchema xsd, XmlSchemaSet xset)
		{
			if ( xsd == null )
				return;

			_errorMessage = "";
			try {
				if ( xset == null ) {
					XmlSchemaSet xsetNew = new XmlSchemaSet();
					xsetNew.ValidationEventHandler += new ValidationEventHandler(SchemaValidationCallback);
					xsetNew.Add(xsd);
					xsetNew.Compile();
					xsetNew.ValidationEventHandler -= new ValidationEventHandler(SchemaValidationCallback);
					xsetNew = null;
				} else {
					xset.ValidationEventHandler += new ValidationEventHandler(SchemaValidationCallback);
					xset.Compile();
					xset.ValidationEventHandler -= new ValidationEventHandler(SchemaValidationCallback);
				}

			} catch ( Exception ex ) {
				if ( !string.IsNullOrEmpty(_errorMessage) ) _errorMessage += Environment.NewLine;
				_errorMessage += ex.Message;
			}
			if ( !string.IsNullOrEmpty(_errorMessage) ) {
				string name = "";
				if ( xsd.Items.Count > 0 ) {
					foreach ( XmlSchemaObject xso in xsd.Items ) {
						XmlSchemaElement xse = xsd.Items[0] as XmlSchemaElement;
						if ( xse != null ) {
							name = xse.Name;
							break;
						}
					}
				}
				_errorMessage = string.Format("Errors in Business Object / XmlSchema{0}{1}{1}{2}",
					string.IsNullOrEmpty(name) ? "." : " \"" + name + "\".",
					Environment.NewLine, _errorMessage);
				MessageBox.Show(_errorMessage, "Error in XmlSchema", MessageBoxButtons.OK, MessageBoxIcon.Error);
			}
		}

		#region DecodeSchema

		private void DecodeSchema(XmlSchemaSet xset, XmlSchema schema, TreeNode node, TreeView tv)
		{
			try {
				XmlSchemaElement xse = null;
				if ( schema != null ) {
					foreach ( XmlSchemaObject xso in schema.Items ) {
						xse = xso as XmlSchemaElement;
						if ( xse != null ) break;
					}
					if ( xse != null ) {
						if ( _dicPrefixNamespace == null ) {
							_dicPrefixNamespace = new Dictionary<string, string>();
						}
						XmlQualifiedName xqnm = new XmlQualifiedName(xse.Name, schema.TargetNamespace);
						XmlSchemaElement xse1 = xset.GlobalElements[xqnm] as XmlSchemaElement;
						Dictionary<string, string> dicCurrentXmlnsPfx = new Dictionary<string, string>();
						if ( xse1 != null ) {
							if ( _dicPrefixNamespace.ContainsKey("tns") ) {
								_dicPrefixNamespace.Add("tns1", xse1.QualifiedName.Namespace);
								dicCurrentXmlnsPfx.Add(xse1.QualifiedName.Namespace, "tns1");
							} else if ( xse1 != null ) {
								_dicPrefixNamespace.Add("tns", xse1.QualifiedName.Namespace);
								dicCurrentXmlnsPfx.Add(xse1.QualifiedName.Namespace, "tns");
							}
							DecodeSchema2(xse1, node, tv, dicCurrentXmlnsPfx);
						}
					}
				}
			} catch ( Exception ex ) {
				MessageBox.Show(ex.Message, "XSLT Mapping", MessageBoxButtons.OK, MessageBoxIcon.Error);
			}
		}

		// stub that wraps the actual decoding in a try block so we can display errors
		private void DecodeSchema(XmlSchema schema, TreeNode node, TreeView tv)
		{
			try {
				if ( schema != null && schema.TargetNamespace == null ) {
					string objnm = "<unknown>";
					foreach ( System.Collections.DictionaryEntry item in schema.Elements ) {
						var qnm = item.Key as XmlQualifiedName;
						if ( qnm != null ) {
							objnm = qnm.Name;
							break;
						}
					}
					throw new Exception("Please set a namespace in schema of the object \"" + objnm + "\".");
				}
				if ( _dicPrefixNamespace == null ) {
					_dicPrefixNamespace = new Dictionary<string, string>();
				}
				Dictionary<string, string> dicCurrentXmlnsPfx = new Dictionary<string, string>();
				if ( _dicPrefixNamespace.ContainsKey("tns") ) {
					_dicPrefixNamespace.Add("tns1", schema.TargetNamespace);
					dicCurrentXmlnsPfx.Add(schema.TargetNamespace, "tns1");
				} else if ( schema != null ) {
					_dicPrefixNamespace.Add("tns", schema.TargetNamespace);
					dicCurrentXmlnsPfx.Add(schema.TargetNamespace, "tns");
				}
				// decode schema
				DecodeSchema2(schema, node, tv, dicCurrentXmlnsPfx);
			} catch ( Exception err ) {
				MessageBox.Show(err.Message, "Mapping", MessageBoxButtons.OK, MessageBoxIcon.Error);
				//edSchemaErrors.Text = err.ToString();
			}
		}

		// recursive decoder
		private TreeNode DecodeSchema2(XmlSchemaObject obj, TreeNode node, TreeView tv,
			Dictionary<string, string> dicNamespacePrefix)
		{
			if ( obj == null )
				return node;

			TreeNode newNode = node;

			// convert the object to all types and then check what type actually exists
			XmlSchemaAnnotation annot = obj as XmlSchemaAnnotation;
			XmlSchemaAttribute attrib = obj as XmlSchemaAttribute;
			XmlSchemaFacet facet = obj as XmlSchemaFacet;
			XmlSchemaDocumentation doc = obj as XmlSchemaDocumentation;
			XmlSchemaAppInfo appInfo = obj as XmlSchemaAppInfo;
			XmlSchemaElement element = obj as XmlSchemaElement;
			XmlSchemaSimpleType simpleType = obj as XmlSchemaSimpleType;
			XmlSchemaComplexType complexType = obj as XmlSchemaComplexType;

			#region DISABLED: GlobalTypes Combobox
			// If the current node is the root node of the tree, then we are
			// possibly adding a global attribute, element, simple type, or complex type.
			//if ( node == tvSchema.Nodes[0] ) {
			//    if ( attrib != null ) {
			//        if ( attrib.Name != null ) {
			//            // add to global list
			//            cbGlobalTypes.Items.Add(new GlobalElementType(attrib.Name, attrib));
			//        }
			//    } else if ( element != null ) {
			//        if ( element.Name != null ) {
			//            // add to global list
			//            cbGlobalTypes.Items.Add(new GlobalElementType(element.Name, element));
			//        }
			//    } else if ( simpleType != null ) {
			//        if ( simpleType.Name != null ) {
			//            // add to global list
			//            cbGlobalTypes.Items.Add(new GlobalElementType(simpleType.Name, simpleType));
			//        }
			//    } else if ( complexType != null ) {
			//        if ( complexType.Name != null ) {
			//            // add to global list
			//            cbGlobalTypes.Items.Add(new GlobalElementType(complexType.Name, complexType));
			//        }
			//    }
			//}
			#endregion

			// if annotation, add a tree node and recurse for documentation and app info
			if ( annot != null ) {
				newNode = new TreeNode("--annotation--");
				newNode.Tag = new TreeNodeInfos(annot);
				//newNode.ImageIndex = 4;
				//newNode.SelectedImageIndex = 4;
				if ( node == null )
					tv.Nodes.Add(newNode);
				else
					node.Nodes.Add(newNode);
				foreach ( XmlSchemaObject schemaObject in annot.Items ) {
					DecodeSchema2(schemaObject, newNode, tv, dicNamespacePrefix);
				}
			} else
				// if attribute, add a tree node
				if ( attrib != null ) {
					newNode = new TreeNode(attrib.QualifiedName.Name);
					newNode.Tag = new TreeNodeInfos(attrib);
					//newNode.ImageIndex = 7;
					//newNode.SelectedImageIndex = 7;
					if ( node == null )
						tv.Nodes.Add(newNode);
					else
						node.Nodes.Add(newNode);
				} else
					// if facet, add a tree node
					if ( facet != null ) {
						//newNode = new TreeNode(facet.ToString());
						//newNode.Tag = new TreeNodeInfos(facet);
						//newNode.ImageIndex = 8;
						//newNode.SelectedImageIndex = 8;
						//if ( node == null )
						//    tv.Nodes.Add(newNode);
						//else
						//    node.Nodes.Add(newNode);
					} else
						// if documentation, add a tree node
						if ( doc != null ) {
							newNode = new TreeNode("--documentation--");
							newNode.Tag = new TreeNodeInfos(doc);
							//newNode.ImageIndex = 5;
							//newNode.SelectedImageIndex = 5;
							if ( node == null )
								tv.Nodes.Add(newNode);
							else
								node.Nodes.Add(newNode);
						} else
							// if app info, add a tree node
							if ( appInfo != null ) {
								newNode = new TreeNode("--app info--");
								newNode.Tag = new TreeNodeInfos(annot);
								//newNode.ImageIndex = 6;
								//newNode.SelectedImageIndex = 6;
								if ( node == null )
									tv.Nodes.Add(newNode);
								else
									node.Nodes.Add(newNode);
							} else
								// if an element, determine whether the element is a simple type or a complex type
								if ( element != null ) {
									TreeNode tnElement = null;
									XmlSchemaSimpleType st = element.SchemaType as XmlSchemaSimpleType;
									XmlSchemaComplexType ct = element.SchemaType as XmlSchemaComplexType;
									if ( st == null && ct == null && annot == null ) {
										st = element.ElementSchemaType as XmlSchemaSimpleType;
										ct = element.ElementSchemaType as XmlSchemaComplexType;
									}
									if ( st != null ) {
										// this is a simple type element.  Recurse.
										TreeNode node2 = DecodeSchema2(st, newNode, tv, dicNamespacePrefix);
										node2.Text = element.Name;
										tnElement = node2;
									} else if ( ct != null ) {
										// this is a complex type element.  Recurse.
										TreeNode node2 = DecodeSchema2(ct, newNode, tv, dicNamespacePrefix);
										if ( !string.IsNullOrEmpty(element.Name) ) {
											node2.Text = element.Name;
										} else if ( element.QualifiedName != null && !string.IsNullOrEmpty(element.QualifiedName.Name) ) {
											node2.Text = element.QualifiedName.Name;
										}
										tnElement = node2;
									} else {
										// This is a plain ol' fashioned element.
										newNode = new TreeNode(element.QualifiedName.Name);
										newNode.Tag = new TreeNodeInfos(element);
										//newNode.ImageIndex = 1;
										//newNode.SelectedImageIndex = 1;
										if ( node == null )
											tv.Nodes.Add(newNode);
										else
											node.Nodes.Add(newNode);
										tnElement = newNode;
									}
									if ( tnElement != null ) {
										// add namespace and prefix to TreeNodeInfos
										TreeNodeInfos tni = (TreeNodeInfos)tnElement.Tag;
										string ns = element.QualifiedName.Namespace;
										if ( string.IsNullOrEmpty(ns) ) {
											ns = getSchemaTargetNamespace(element);
										}
										if ( string.IsNullOrEmpty(tni.DataType) ) {
											if ( st != null && st.Datatype != null ) {
												tni.DataType = st.Datatype.TypeCode.ToString();
											} else if ( ct != null && !ct.QualifiedName.IsEmpty ) {
												tni.DataType = ct.QualifiedName.ToString();
											}
										}
										tni.Namespace = ns;
										if ( !dicNamespacePrefix.ContainsKey(ns) ) {
											string prefix = string.Format("q{0}", _dicPrefixNamespace.Count - 1);
											dicNamespacePrefix.Add(ns, prefix);
											_dicPrefixNamespace.Add(prefix, ns);
										}
										tni.Prefix = dicNamespacePrefix[ns];
										if ( string.IsNullOrEmpty(tni.DataType) ) {
											tnElement.ToolTipText =
												string.Format("Name:\t\t{1}:{2}{0}Namespace:\t{3}",
													Environment.NewLine, tni.Prefix, tnElement.Text, ns);
										} else {
											tnElement.ToolTipText =
												string.Format("Name:\t\t{1}:{2}{0}Namespace:\t{3}{0}DataType:\t{4}",
													Environment.NewLine, tni.Prefix, tnElement.Text, ns, tni.DataType);
										}
									}
								} else
									// if a simple type, then add a tree node and recurse facets
									if ( simpleType != null ) {
										newNode = new TreeNode(simpleType.QualifiedName.Name);
										newNode.Tag = new TreeNodeInfos(simpleType);
										//newNode.ImageIndex = 2;
										//newNode.SelectedImageIndex = 2;
										if ( node == null )
											tv.Nodes.Add(newNode);
										else
											node.Nodes.Add(newNode);
										XmlSchemaSimpleTypeRestriction rest = simpleType.Content as XmlSchemaSimpleTypeRestriction;
										if ( rest != null ) {
											foreach ( XmlSchemaFacet schemaObject in rest.Facets ) {
												DecodeSchema2(schemaObject, newNode, tv, dicNamespacePrefix);
											}
										}
									} else
										// if a complex type, add a tree node and recurse its sequence
										if ( complexType != null ) {
											newNode = new TreeNode(complexType.Name);
											newNode.Tag = new TreeNodeInfos(complexType);
											//newNode.ImageIndex = 3;
											//newNode.SelectedImageIndex = 3;
											if ( node == null )
												tv.Nodes.Add(newNode);
											else
												node.Nodes.Add(newNode);

											XmlSchemaSequence seq = complexType.Particle as XmlSchemaSequence;
											XmlSchemaChoice choice = complexType.Particle as XmlSchemaChoice;
											if ( seq != null ) {
												foreach ( XmlSchemaObject schemaObject in seq.Items ) {
													DecodeSchema2(schemaObject, newNode, tv, dicNamespacePrefix);
												}
											} else if ( choice != null ) {
												foreach ( XmlSchemaObject schemaObj in choice.Items ) {
													DecodeSchema2(schemaObj, newNode, tv, dicNamespacePrefix);
												}
											}
										}

			// now recurse any object collection of the type.
			foreach ( PropertyInfo property in obj.GetType().GetProperties() ) {
				if ( property.PropertyType.FullName == "System.Xml.Schema.XmlSchemaObjectCollection" ) {
					XmlSchemaObjectCollection childObjectCollection = (XmlSchemaObjectCollection)property.GetValue(obj, null);
					foreach ( XmlSchemaObject schemaObject in childObjectCollection ) {
						if ( obj is XmlSchema ) {
							if ( schemaObject is XmlSchemaElement )
								DecodeSchema2(schemaObject, newNode, tv, dicNamespacePrefix);
						} else {
							DecodeSchema2(schemaObject, newNode, tv, dicNamespacePrefix);
						}
					}
				}
			}
			return newNode;
		}

		private string getSchemaTargetNamespace(XmlSchemaElement element)
		{
			XmlSchema xsd = null;
			XmlSchemaObject xsobj = element;
			do {
				if ( xsobj.Parent != null ) {
					xsobj = xsobj.Parent;
				} else {
					xsd = null;
					break;
				}
				xsd = xsobj as XmlSchema;
			} while ( xsd == null );
			if ( xsd != null ) {
				return xsd.TargetNamespace;
			} else {
				return "";
			}
		}
		#endregion

		private bool checkCompatibleNodes(TreeNode source, TreeNode target)
		{
			if ( MapperControl.ModifierKeys == Keys.Alt ) return true;
			TreeNodeInfos sourceInfo = source.Tag as TreeNodeInfos;
			TreeNodeInfos targetInfo = target.Tag as TreeNodeInfos;
			bool isSourceEmpty = string.IsNullOrEmpty(sourceInfo.DataType);
			bool isTargetEmpty = string.IsNullOrEmpty(targetInfo.DataType);
			bool isSourceComplex = sourceInfo.DataType.StartsWith("http://");
			bool isTargetComplex = targetInfo.DataType.StartsWith("http://");
			return ( isSourceEmpty && isTargetEmpty ) || ( isSourceEmpty && isTargetComplex ) ||
				( isSourceComplex && isTargetComplex ) || ( isSourceComplex && isTargetEmpty ) ||
				sourceInfo.DataType == targetInfo.DataType;
		}

		private string writeMappingXml()
		{
			if ( _mapperSettings != null ) {
				XmlDocument xd = new XmlDocument();
				using ( StringWriter sw = new StringWriter() ) {
					XmlSerializer xs = new XmlSerializer(typeof(MapperSettings));
					xs.Serialize(sw, _mapperSettings);
					xd.LoadXml(sw.ToString());
				}
				return xd.DocumentElement.OuterXml;
			} else {
				return null;
			}
		}
		private void readMappingXml(string mapping)
		{
			if ( mapping == null || mapping == "" )
				return;


			if ( mapping.StartsWith("<ArrayOfMappingItem") ) {
				_mapperSettings = new MapperSettings();

				MappingItem[] loadedMapping = null;
				try {
					using ( StringReader sr = new StringReader(mapping) ) {
						XmlSerializer xs = new XmlSerializer(typeof(MappingItem[]));
						loadedMapping = (MappingItem[])xs.Deserialize(sr);
					}
				} catch {
					loadedMapping = null;
				}
				if ( loadedMapping == null || loadedMapping.Length == 0 )
					return;

				_mapperSettings.IsCustomXSLT = false;
				_mapperSettings.MappingItems.AddRange(loadedMapping);
			} else {
				try {
					using ( StringReader sr = new StringReader(mapping) ) {
						XmlSerializer xs = new XmlSerializer(typeof(MapperSettings));
						_mapperSettings = (MapperSettings)xs.Deserialize(sr);
					}
				} catch {
					_mapperSettings = new MapperSettings();
				}
			}
			List<TreeNode> lstSource = getFlatTree(treeSource);
			List<TreeNode> lstTarget = getFlatTree(treeTarget);

			MappingItem[] mappingitems = _mapperSettings.MappingItems.ToArray();
			_mapperSettings.MappingItems.Clear();
			foreach ( MappingItem mi in mappingitems ) {
				foreach ( TreeNode tnS in lstSource ) {
					if ( tnS.FullPath == mi.SourcePath ) {
						foreach ( TreeNode tnT in lstTarget ) {
							if ( tnT.FullPath == mi.TargetPath )
								setNewMapping(tnS, tnT, true);
						}
					}
				}
			}
			this.Refresh();
		}
		private List<TreeNode> getFlatTree(TreeView tv)
		{
			int i = 0;
			List<TreeNode> lst = new List<TreeNode>();
			foreach ( TreeNode tn in tv.Nodes ) lst.Add(tn);
			while ( i < lst.Count ) {
				if ( lst[i].Nodes.Count > 0 )
					foreach ( TreeNode tn in lst[i].Nodes ) lst.Add(tn);
				i++;
			}
			return lst;
		}

		//private BitmapCursor createDragCursor(DragDropEffects effect, TreeNode node)
		//{
		//    int width = node.Bounds.Width;
		//    int height = node.Bounds.Height;
		//    Rectangle r = new Rectangle(0, 0, width, height);
		//    using ( Graphics g0 = CreateGraphics() )
		//    using ( Bitmap bmp = new Bitmap(width, height * 4, g0) )
		//    using ( Graphics g = Graphics.FromImage(bmp) ) {
		//        g.Clear(Color.FromArgb(0, 0, 0, 0));

		//        Color cb1 = Color.FromArgb(255, SystemColors.Highlight);
		//        Color cb2 = Color.FromArgb(64, SystemColors.Highlight);
		//        using ( Brush b = new System.Drawing.Drawing2D.LinearGradientBrush(r, cb1, cb2, 0, false) )
		//            g.FillRectangle(b, 0, 0, width, height);

		//        float textxpos = 0.0f;
		//        if ( node.TreeView != null && node.TreeView.ImageList != null ) {
		//            if ( node.ImageIndex >= 0 ) {
		//                node.TreeView.ImageList.Draw(g, 0, 0, node.ImageIndex);
		//                textxpos = node.TreeView.ImageList.ImageSize.Width;
		//            } else if ( !string.IsNullOrEmpty(node.ImageKey) ) {
		//                node.TreeView.ImageList.Draw(g, 0, 0, node.ImageIndex);
		//                textxpos = node.TreeView.ImageList.ImageSize.Width;
		//            } else {
		//                node.TreeView.ImageList.Draw(g, 0, 0, 0);
		//                textxpos = node.TreeView.ImageList.ImageSize.Width;
		//            }
		//        }

		//        Color ct1 = Color.FromArgb(255, SystemColors.HighlightText);
		//        Color ct2 = Color.FromArgb(64, SystemColors.HighlightText);
		//        using ( Brush b = new System.Drawing.Drawing2D.LinearGradientBrush(r, ct1, ct2, 0, false) )
		//            g.DrawString(node.Text, Font, b, textxpos, 0);

		//        if ( effect == DragDropEffects.None ) {
		//            return new BitmapCursor(bmp, Cursors.No);
		//        } else {
		//            return new BitmapCursor(bmp, Cursors.Default);
		//        }
		//    }
		//}

		private void showMapDiffToolTip(TreeViewHitTestInfo hit)
		{
			if ( toolTips.Tag == hit.Node ) return;	// EXIT

			toolTips.Tag = hit.Node;
			toolTips.ToolTipIcon = ToolTipIcon.Info;
			toolTips.ToolTipTitle = "Mapping of different data types!";
			toolTips.Show(
				string.Format(
					"You are attempting to perform a data mapping between two different data types.{0}" +
					"You can press the ALT key to force the creation of this mapping.",
					Environment.NewLine),
				hit.Node.TreeView, hit.Node.Bounds.Left, hit.Node.Bounds.Bottom);
		}
		private void hideMapDiffToolTip(TreeView tv)
		{
			if ( toolTips.Tag == null ) return;	// EXIT

			toolTips.Hide(tv);
			toolTips.Tag = null;
		}

		#endregion

		#endregion


	}
}
